<?php

namespace App\Task;


use App\Common\Log;
use App\Common\SysContext;
use App\Traits\TraitConnectDB;
use EasySwoole\Component\Timer;
use Swoole\Coroutine\Channel;

class TaskBase
{
    use TraitConnectDB;


    /**
     * @return TaskBase
     */
    static public function funcIns()
    {
        return new static();
    }

    /**
     * 协程上下文
     *
     * @return SysContext
     */
    protected function context()
    {
        return SysContext::funcInsStatic();
    }


    private $pub_handle_count = 0;


    /**
     * 创建协程最大值，跟内存，连接池有关
     *
     * @return int
     */
    private function funcGetCoMax()
    {
        return 100;
    }

    /**
     * 任务创建协程数
     *
     * @return int
     */
    public function funcGetCoCount()
    {
        return 50;
    }

    /**
     * 回调等待时间(单位：毫秒)
     *
     * @return int
     */
    public function funcGetCallBack__waitMS()
    {
        return 3000;
    }


    public function funcRun()
    {
        $this->funcCenter();
    }


    private function funcCenter()
    {
        Log::funcLogTask(__METHOD__, static::class . '-BEGIN');


        //任务创建协程数
        $co_count = $this->funcGetCoCount();
        if ($co_count > $this->funcGetCoMax())
        {
            $co_count = $this->funcGetCoMax();
        }

        Log::funcLogTask(__METHOD__, static::class . ";创建协程数={$co_count}");


        $this->pub_handle_count = 0;

        $co_channel = new Channel($co_count);


        $arr_cid = [];
        for ($i = 0; $i < $co_count; $i++)
        {
            $cid = go(function () use ($i, $co_channel)
            {
                $handle_count = 0;

                try
                {
                    $handle_count = $this->funcHandle($i);
                }
                catch (\Exception $e)
                {
                    Log::funcLogErr(__METHOD__, $e->getMessage());
                }

                defer(function () use ($co_channel, $handle_count)
                {
                    $this->funcHandler__after();

                    //销毁当前协程所有值
                    $this->context()->destroy();

                    //标记完成
                    $co_channel->push($handle_count);
                });
            });

            $arr_cid[] = $cid;
        }


        //检查是否全部协程全部处理完成
        go(function () use ($co_channel)
        {
            while (true)
            {
                if ($co_channel->isFull())
                {
                    break;
                }

                \co::sleep(1);
            }


            $this->funcDone();


            for ($i = 0; $i < $co_channel->length(); $i++)
            {
                $handle_count = $co_channel->pop(1);
                if (is_int($handle_count))
                {
                    $this->pub_handle_count += $handle_count;
                }
            }

            $co_channel->close();


            Log::funcLogTask(__METHOD__, static::class . '-END');

            //回调
            $wait = $this->pub_handle_count == 0;
            $this->funcCallBack($wait);
        });
    }


    public function funcHandle($index)
    {
        return 0;
    }

    /**
     * 协程事件完成，在关闭之前会调用
     */
    public function funcHandler__after()
    {

    }


    /**
     * 所有协程事件完成之后调用
     */
    public function funcDone()
    {

    }


    /**
     * 回调
     *
     * @param bool $wait
     */
    private function funcCallBack($wait = true)
    {
        if ($wait)
        {
            //休息x毫秒，再回调
            Log::funcLogTask(__METHOD__, "休息{$this->funcGetCallBack__waitMS()}毫秒，再回调");
            Timer::getInstance()->after($this->funcGetCallBack__waitMS(), function ()
            {
                $this->funcRun();
            });
        }
        else
        {
            //立刻回调
            Log::funcLogTask(__METHOD__, '立刻回调');
            $this->funcRun();
        }
    }
}